package com.yueka.utils;

import com.yueka.pojo.entity.course.Course;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 课程冲突检测工具类（区分新课程和已有课程的排课起始点）
 */
public class CourseConflictDetector {

    // 存储最新的冲突信息
    public static String message;
    
    // 最大检测天数（防止无限循环）
    private static final int MAX_FUTURE_DAYS = 365 * 2; // 2年

    private static final DateTimeFormatter DATE_TIME_FORMAT =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter TIME_FORMAT =
        DateTimeFormatter.ofPattern("HH:mm:ss");
    private static final DateTimeFormatter TIME_DISPLAY_FORMAT =
        DateTimeFormatter.ofPattern("HH:mm");
    private static final DateTimeFormatter DATE_FORMAT =
        DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 检测课程冲突
     * @param newCourse 新课程
     * @param existingCourses 已有课程列表
     * @return 冲突课程列表
     */
    public static List<Course> detectConflicts(Course newCourse, List<Course> existingCourses) {
        List<Course> conflicts = new ArrayList<>();
        message = null;

        if (newCourse == null || existingCourses == null) {
            message = "课程参数不能为空";
            return conflicts;
        }

        // 计算新课程剩余课时
        int newRemainingLessons = calculateRemainingLessons(newCourse);
        if (newRemainingLessons <= 0) {
            message = "新课程没有剩余课时";
            return conflicts;
        }

        // 获取新课程的起始日期（使用start_time字段）
        LocalDate newCourseStartDate = getStartDateForNewCourse(newCourse);
        if (newCourseStartDate == null) {
            message = "新课程起始日期无效";
            return conflicts;
        }

        // 生成新课程未来上课日期
        List<LocalDate> newCourseDates = generateFutureCourseDates(newCourse, newRemainingLessons, newCourseStartDate);
        if (newCourseDates.isEmpty()) {
            message = "无法生成新课程的上课日期";
            return conflicts;
        }

        // 解析新课程时间
        LocalTime newStartTime = parseScheduleTime(newCourse);
        if (newStartTime == null) {
            message = "新课程时间格式错误";
            return conflicts;
        }
        LocalTime newEndTime = newStartTime.plusMinutes(newCourse.getDuration());

        // 检测与每个已有课程的冲突
        for (Course existingCourse : existingCourses) {
            if (existingCourse == null) continue;

            // 计算已有课程剩余课时
            int existingRemaining = calculateRemainingLessons(existingCourse);
            if (existingRemaining <= 0) continue;

            // 获取已有课程的起始日期（使用系统当前时间）
            LocalDate existingStartDate = LocalDate.now();
            
            // 生成已有课程未来上课日期
            List<LocalDate> existingCourseDates = generateFutureCourseDates(existingCourse, existingRemaining, existingStartDate);
            if (existingCourseDates.isEmpty()) continue;

            // 解析已有课程时间
            LocalTime existingStartTime = parseScheduleTime(existingCourse);
            if (existingStartTime == null) continue;
            LocalTime existingEndTime = existingStartTime.plusMinutes(existingCourse.getDuration());

            // 检查日期和时间冲突
            if (hasConflict(newCourseDates, newStartTime, newEndTime,
                           existingCourseDates, existingStartTime, existingEndTime)) {
                conflicts.add(existingCourse);
                message = generateConflictMessage(newCourse, existingCourse, 
                                                 newCourseDates.get(0), newStartTime, newEndTime);
            }
        }

        return conflicts;
    }

    /**
     * 获取新课程的起始日期（优先使用start_time字段）
     */
    private static LocalDate getStartDateForNewCourse(Course newCourse) {
        // 单次排课：从schedule_time解析日期
        if ("1".equals(newCourse.getScheduleType())) {
            try {
                return LocalDate.parse(newCourse.getScheduleTime(), DATE_TIME_FORMAT);
            } catch (Exception e) {
                return null;
            }
        }
        
        // 重复排课：使用start_time字段
        if (newCourse.getStartTime() != null) {
            return newCourse.getStartTime().toLocalDate();
        }
        
        // 默认使用当前日期
        return LocalDate.now();
    }

    /**
     * 计算剩余课时
     */
    private static int calculateRemainingLessons(Course course) {
        if (course.getTotalLessons() == null || course.getFinishedLessons() == null) {
            return 0;
        }
        return course.getTotalLessons() - course.getFinishedLessons();
    }

    /**
     * 解析课程时间（只解析时间部分）
     */
    private static LocalTime parseScheduleTime(Course course) {
        try {
            if (course.getScheduleTime() == null) {
                return null;
            }

            if ("1".equals(course.getScheduleType())) {
                // 单次排课：解析完整日期时间，但只取时间部分
                return LocalTime.parse(course.getScheduleTime(), TIME_FORMAT);
            } else {
                // 重复排课：直接解析时间部分
                return LocalTime.parse(course.getScheduleTime(), TIME_FORMAT);
            }
        } catch (DateTimeParseException e) {
            return null;
        }
    }

    /**
     * 生成未来上课日期
     */
    private static List<LocalDate> generateFutureCourseDates(Course course, int remainingLessons, LocalDate startDate) {
        List<LocalDate> dates = new ArrayList<>();
        LocalDate currentDate = startDate;
        int daysChecked = 0;
        int lessonsAdded = 0;

        // 单次排课
        if ("1".equals(course.getScheduleType())) {
            // 单次课只有一个上课日期
            if (remainingLessons > 0) {
                dates.add(currentDate);
                lessonsAdded++;
            }
            return dates;
        }

        // 每天重复排课
        if ("1".equals(course.getRepeatType())) {
            while (lessonsAdded < remainingLessons && daysChecked < MAX_FUTURE_DAYS) {
                dates.add(currentDate);
                lessonsAdded++;
                currentDate = currentDate.plusDays(1);
                daysChecked++;
            }
            return dates;
        }

        // 每周重复排课
        if ("2".equals(course.getRepeatType()) && course.getRepeatWeekly() != null) {
            // 解析每周重复的星期几
            Set<Integer> repeatDays = parseRepeatWeekly(course.getRepeatWeekly());
            if (repeatDays.isEmpty()) return dates;
            
            while (lessonsAdded < remainingLessons && daysChecked < MAX_FUTURE_DAYS) {
                int dayOfWeek = convertToStandardDayOfWeek(currentDate.getDayOfWeek());
                
                if (repeatDays.contains(dayOfWeek)) {
                    dates.add(currentDate);
                    lessonsAdded++;
                }
                
                currentDate = currentDate.plusDays(1);
                daysChecked++;
            }
            return dates;
        }

        return dates;
    }

    /**
     * 解析每周重复设置
     */
    private static Set<Integer> parseRepeatWeekly(String repeatWeekly) {
        if (repeatWeekly == null || repeatWeekly.trim().isEmpty()) {
            return new HashSet<>();
        }
        
        return Arrays.stream(repeatWeekly.split(","))
            .map(String::trim)
            .filter(s -> !s.isEmpty())
            .map(Integer::parseInt)
            .collect(Collectors.toSet());
    }

    /**
     * 转换到标准星期表示 (1=周一, 7=周日)
     */
    private static int convertToStandardDayOfWeek(DayOfWeek dayOfWeek) {
        // Java的DayOfWeek: MONDAY=1, SUNDAY=7
        return dayOfWeek.getValue();
    }

    /**
     * 检查是否有冲突
     */
    private static boolean hasConflict(List<LocalDate> dates1, LocalTime start1, LocalTime end1,
                                     List<LocalDate> dates2, LocalTime start2, LocalTime end2) {
        // 找出共同的日期
        Set<LocalDate> commonDates = new HashSet<>(dates1);
        commonDates.retainAll(dates2);
        
        // 没有共同日期，不可能冲突
        if (commonDates.isEmpty()) return false;
        
        // 检查时间段是否重叠
        return hasTimeOverlap(start1, end1, start2, end2);
    }

    /**
     * 检查时间段是否有重叠
     */
    private static boolean hasTimeOverlap(LocalTime start1, LocalTime end1,
                                        LocalTime start2, LocalTime end2) {
        return !(end1.isBefore(start2) || end2.isBefore(start1));
    }

    /**
     * 生成冲突信息
     */
    private static String generateConflictMessage(Course newCourse, Course existingCourse,
                                                LocalDate exampleDate, LocalTime startTime, LocalTime endTime) {
        String newCourseType = getCourseTypeDescription(newCourse);
        String existingCourseName = existingCourse.getCourseName();
        String timeRange = formatTimeRange(startTime, endTime);
        
        if ("1".equals(newCourse.getScheduleType())) {
            return String.format("与课程《%s》时间冲突：%s %s（单次课）",
                existingCourseName, exampleDate.format(DATE_FORMAT), timeRange);
        }
        
        if ("1".equals(newCourse.getRepeatType())) {
            return String.format("与课程《%s》时间冲突：从%s开始每天%s（重复课）",
                existingCourseName, newCourse.getStartTime().toLocalDate().format(DATE_FORMAT), timeRange);
        }
        
        if ("2".equals(newCourse.getRepeatType())) {
            String weekDayNames = getWeekDaysDescription(newCourse.getRepeatWeekly());
            return String.format("与课程《%s》时间冲突：从%s开始每周%s %s（重复课）",
                existingCourseName, newCourse.getStartTime().toLocalDate().format(DATE_FORMAT), weekDayNames, timeRange);
        }
        
        return "发现时间冲突";
    }

    /**
     * 获取课程类型描述
     */
    private static String getCourseTypeDescription(Course course) {
        if ("1".equals(course.getScheduleType())) return "单次课";
        if ("1".equals(course.getRepeatType())) return "每天重复课";
        if ("2".equals(course.getRepeatType())) return "每周重复课";
        return "未知类型";
    }

    /**
     * 获取星期描述
     */
    private static String getWeekDaysDescription(String repeatWeekly) {
        if (repeatWeekly == null) return "";
        
        Set<Integer> days = parseRepeatWeekly(repeatWeekly);
        if (days.isEmpty()) return "";
        
        String[] dayNames = {"一", "二", "三", "四", "五", "六", "日"};
        return days.stream()
            .sorted()
            .map(day -> {
                if (day >= 1 && day <= 7) {
                    return "周" + dayNames[day - 1];
                }
                return "";
            })
            .filter(s -> !s.isEmpty())
            .collect(Collectors.joining(","));
    }

    /**
     * 格式化时间范围
     */
    private static String formatTimeRange(LocalTime startTime, LocalTime endTime) {
        return startTime.format(TIME_DISPLAY_FORMAT) + "-" +
               endTime.format(TIME_DISPLAY_FORMAT);
    }
}